package com.atguigu.tingshu.account.listener;

import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.common.constant.RabbitConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.order.client.OrderInfoFeignClient;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.rabbitmq.client.Channel;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

@Component
public class AccountListener {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 订单创建成功，余额扣减监听器
     *
     * @param orderNo
     * @param channel
     * @param message
     * @throws IOException
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(RabbitConstant.REDUCE_ACCOUNT_QUEUE),
            exchange = @Exchange(value = RabbitConstant.ORDER_EXCHANGE, type = ExchangeTypes.TOPIC, ignoreDeclarationExceptions = "true"),
            key = {RabbitConstant.REDUCE_ACCOUNT_RK}
    ))
    public void reduceAccount(String orderNo, Channel channel, Message message) throws IOException {
        if (StringUtils.isBlank(orderNo)) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        // 消息重复消费的幂等性校验
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(RedisConstant.ACCOUNT_REDUCE_PREFIX + orderNo,
                orderNo,
                RedisConstant.CACHE_TEMPORARY_TIMEOUT,
                TimeUnit.SECONDS);
        if (!flag) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        // 查询redis，获取扣减余额的缓存信息
        String accountLockCacheKey = RedisConstant.ACCOUNT_CHECK_INFO_PREFIX + orderNo;
        AccountLockResultVo resultVo = (AccountLockResultVo) redisTemplate.opsForValue().get(accountLockCacheKey);
        if (resultVo == null) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        int result = userAccountMapper.reduceAmount(resultVo.getUserId(), resultVo.getAmount());
        if (result == 0) {
            // 如果扣减余额失败，抛出异常，消息重新入队
            redisTemplate.delete(RedisConstant.ACCOUNT_REDUCE_PREFIX + orderNo);
            throw new GuiguException(ResultCodeEnum.DATA_ERROR);
        }
        // 调用订单服务的远程接口，修改订单的支付状态为已支付，并处理用户服务中订单购买的内容
        Result updateOrderStatusResult = orderInfoFeignClient.updateOrderStatus(orderNo);
        Assert.notNull(updateOrderStatusResult, "订单{" + orderNo + "}的状态修改失败");
        // 记录扣减余额的操作记录
        UserAccountDetail userAccountDetail = UserAccountDetail.builder()
                .userId(resultVo.getUserId())
                .title("用户余额扣减")
                .tradeType(SystemConstant.ACCOUNT_TRADE_TYPE_MINUS)
                .amount(resultVo.getAmount())
                .build();
        Db.save(userAccountDetail);
        // 删除锁定信息的缓存
        redisTemplate.delete(accountLockCacheKey);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(RabbitConstant.RELEASE_ACCOUNT_QUEUE),
            exchange = @Exchange(value = RabbitConstant.ORDER_EXCHANGE, type = ExchangeTypes.TOPIC, ignoreDeclarationExceptions = "true"),
            key = {RabbitConstant.RELEASE_ACCOUNT_RK}
    ))
    public void releaseAmount(String orderNo, Channel channel, Message message) throws IOException {
        if (StringUtils.isBlank(orderNo)) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        // 消息重复消费的幂等性校验
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(RedisConstant.ACCOUNT_RELEASE_PREFIX + orderNo,
                orderNo,
                RedisConstant.CACHE_TEMPORARY_TIMEOUT,
                TimeUnit.SECONDS);
        if (!flag) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        // 查询redis，获取释放余额的缓存信息
        String accountLockCacheKey = RedisConstant.ACCOUNT_CHECK_INFO_PREFIX + orderNo;
        AccountLockResultVo resultVo = (AccountLockResultVo) redisTemplate.opsForValue().get(accountLockCacheKey);
        if (resultVo == null) {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        int result = userAccountMapper.releaseAmount(resultVo.getUserId(), resultVo.getAmount());
        if (result == 0) {
            redisTemplate.delete(RedisConstant.ACCOUNT_REDUCE_PREFIX + orderNo);
            throw new GuiguException(ResultCodeEnum.DATA_ERROR);
        }
        // 记录释放余额的操作记录
        UserAccountDetail userAccountDetail = UserAccountDetail.builder()
                .userId(resultVo.getUserId())
                .title("用户锁定余额释放")
                .tradeType(SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK)
                .amount(resultVo.getAmount())
                .build();
        Db.save(userAccountDetail);
        // 删除锁定信息的缓存
        redisTemplate.delete(accountLockCacheKey);
    }
}